#ifndef VALUE_VISITORS_HPP
#define VALUE_VISITORS_HPP

/**
 * \file value_visitors.hpp
 * implements various value visitors that give 
 * useful functionality for values
 */

namespace BMD
{
  //! prints values
  class output_visitor : public static_visitor<>
  {
  public:
//    output_visitor() : out(cout), print_full(false) { }
    output_visitor(ostream& o = cout, bool _print_full = false) :
      out(o), print_full(_print_full) { }

    void operator() (int i) const;
    void operator() (double d) const;
    void operator() (string s) const;
    void operator() (ListRef l) const;
    void operator() (BlockRef a) const;
    void operator() (const NullValue& nv) const;

    ostream& out;

  private:
    bool print_full;

    void print_as_tuple(BlockRef b, char start_char, char end_char) const;
    void print_reference(BlockRef b) const;
  };

  /** 
   * determines if a value is of a particular type
   */
  template <typename T>
  class is_type_visitor : public static_visitor<bool>
  {
  public:
    bool operator() (T t) const { return true; }
    template <typename U>
    bool operator() (U u) const { return false; }
  };

  /** 
   * casts the value to a specified type
   * and throws a type error if the value is of an incorrect type
   */
  template <typename T>
  class assert_type_visitor : public static_visitor<T>
  {
  public:
    T operator() (T t) const { return t; }
    T operator() (const NullValue& nv) const
    {
      throw null_value_error("");
    }
    template <typename U>
    T operator() (const U& u) const { throw type_error("incorrect type"); }
  };

  /**
   * assigns a name to a given type
   */
  class typename_visitor : public static_visitor<string>
  {
  public:
    string operator() (int i) const { return "int"; }
    string operator() (double d) const {return "float"; }
    string operator() (string s) const { return "string"; }
    string operator() (ListRef l) const { return "list"; }
    string operator() (BlockRef a) const;
    string operator() (const NullValue& nv) const { return "null-value"; }
  };

  /** determines equality of given values */
  class equals_visitor : public static_visitor<bool>
  {
  public:
    bool operator() (int a, int b) const { return a == b; }
    bool operator() (double a, double b) const { return a == b; }
    bool operator() (int a, double b) const { return a == b; }
    bool operator() (double a, int b) const { return a == b; }
    bool operator() (string a, string b) const { return a == b; }
    bool operator() (ListRef a, ListRef b) const { return a == b; }
    bool operator() (BlockRef a, BlockRef b) const { return a == b; }
    bool operator() (NullValue a, NullValue b) const { return true; }
    
    template <typename U, typename V>
    bool operator() (const U& u, const V& v) const { return false; }
  };

  /** 
   * clones the given type
   */
  class clone_visitor : public boost::static_visitor<Value>
  {
  public:
    clone_visitor(bool _deep) : deep(_deep) { }
    
    Value operator() (int i) const;
    Value operator() (double d) const;
    Value operator() (string s) const;
    Value operator() (ListRef l) const;
    Value operator() (BlockRef a) const;
    Value operator() (NullValue nv) const;

  private:
    bool deep;
  };  

  /**
   * retrives the length of a value where having a length makes sense
   */
  class length_visitor : public boost::static_visitor<unsigned int>
  {
  public:
    unsigned int operator()(string s) const
    {
      return s.size();
    }
    unsigned int operator()(ListRef l) const
    {
      if (l.get() == 0)
        return 0;
      return l->length();
    }
    unsigned int operator()(BlockRef b) const
    {
      return b->get_size();
    }
    UNARY_DEFAULT_TO_TYPE_ERROR("length", unsigned int, "expects a length, string, or block")
  };
}

#endif
